package org.ponly.cta4j.service.store.redis;

import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import org.ponly.cta4j.Captcha;
import org.ponly.cta4j.service.store.CaptchaStore;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;


import static org.ponly.cta4j.service.store.redis.SerializeUtils.deserialize;
import static org.ponly.cta4j.service.store.redis.SerializeUtils.serialize;

/**
 * Redis Store.
 *
 * @author vacoor
 * @since 1.0
 */
public class RedisStore implements CaptchaStore {
    /**
     * default charset UTF-8.
     */
    private static final Charset UTF8 = Charset.forName("UTF-8");

    /**
     * 默认缓存名称.
     */
    private static final String DEFAULT_CACHE_PREFIX = "cta4j.store.cache.";

    private JedisPool jedisPool;
    private final String keyPrefix;

    /**
     * constructor.
     *
     * @param jedisPool jedis pool
     */
    public RedisStore(final JedisPool jedisPool) {
        this(jedisPool, DEFAULT_CACHE_PREFIX);
    }

    /**
     * constructor.
     *
     * @param jedisPool jedis pool
     * @param keyPrefix cache key prefix
     */
    public RedisStore(final JedisPool jedisPool, final String keyPrefix) {
        this.jedisPool = jedisPool;
        this.keyPrefix = keyPrefix;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Captcha get(final String id) {
        final Jedis jedis = getJedis();
        final byte[] key = getBytesKey(id);
        try {
            final byte[] bytes = jedis.get(key);
            return (Captcha) deserialize(bytes);
        } finally {
            releaseJedis(jedis);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void put(final String id, final Captcha captcha) {
        final Jedis jedis = getJedis();
        final byte[] key = getBytesKey(id);
        try {
            jedis.set(key, serialize(captcha));
        } finally {
            releaseJedis(jedis);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void remove(final String id) {
        final Jedis jedis = getJedis();
        final byte[] key = getBytesKey(id);
        try {
            // deserialize(jedis.get(key));
            jedis.del(key);
        } finally {
            releaseJedis(jedis);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long size() {
        final Jedis jedis = getJedis();
        final byte[] allKey = getBytesKey("*");
        try {
            if (!hasKeyPrefix()) {
                return jedis.dbSize();
            } else {
                return jedis.keys(allKey).size();
            }
        } finally {
            releaseJedis(jedis);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<String> keys() {
        final Jedis jedis = getJedis();
        // final byte[] allKey = getBytesKey(keyPrefix + "*");
        final String allKey = getKey("*");
        try {
            // Set<byte[]> keys = jedis.keys(allKey);
            final Set<String> keys = new TreeSet<String>();
            for (final String key : jedis.keys(allKey)) {
                keys.add(key.substring(keyPrefix.length()));
            }
            return keys;
        } finally {
            releaseJedis(jedis);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clear() {
        final Jedis jedis = getJedis();
        final byte[] allKey = getBytesKey("*");
        try {
            //
            if (!hasKeyPrefix()) {
                jedis.flushDB();
            } else {
                final Set<byte[]> keys = jedis.keys(allKey);
                if (0 < keys.size()) {
                    jedis.del(keys.toArray(new byte[keys.size()][]));
                }
            }
        } finally {
            releaseJedis(jedis);
        }
    }

    /**
     * @return 当前 store key 是否有缓存前缀
     */
    protected boolean hasKeyPrefix() {
        return null != keyPrefix && !"".equals(keyPrefix);
    }

    /**
     * 获取缓存的 key.
     *
     * @param id id
     * @return cache key
     */
    protected String getKey(final String id) {
        return hasKeyPrefix() ? keyPrefix + id : id;
    }

    /**
     * 获取缓存的 字节 key.
     *
     * @param id id
     * @return cache key
     */
    protected byte[] getBytesKey(final String id) {
        return getKey(id).getBytes(UTF8);
    }

    /**
     * @return redis 连接.
     */
    protected Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * 释放给定连接.
     *
     * @param jedis jedis
     */
    protected void releaseJedis(final Jedis jedis) {
        jedis.close();
    }
}
